VPCピアリングを使って別アカウントにあるVPC内のRDSに接続できるようにする設定をCloudFormationで作成してみた
VPCピアリングを使って同じアカウント内のVPCを繋げたことはあったのですが、別アカウントにあるVPCは繋げたことが無かったのでやってみました。
構成
構成図は以下になります。
アカウントAのプライベートサブネットに配置されているEC2からアカウントBのVPCへの通信 (RDS Aurora MySQLへのアクセスなど) をVPCピアリング経由で行う構成となっています。
それ以外の通信はNATゲートウェイを経由して通信を行います。
設定してみた
アカウントAでVPCピアリング以外のリソース作成
まずはアカウントAでVPCピアリング以外のリソースを作成します。
リソース作成には以下のCloudFormationテンプレートを使用しました。
CloudFormationテンプレート (ここをクリックしてください)
AWSTemplateFormatVersion: "2010-09-09" Description: Network and EC2 Stack Metadata: # ------------------------------------------------------------# # Metadata # ------------------------------------------------------------# AWS::CloudFormation::Interface: ParameterGroups: - Label: default: Parameters for VPC Parameters: - VPCCIDR - Label: default: Parameters for Subnet Parameters: - PublicSubnet01CIDR - PrivateSubnet01CIDR - Label: default: Parameters for ec2 Parameters: - EC2VolumeSize - EC2VolumeIOPS - EC2AMI - EC2InstanceType Parameters: # ------------------------------------------------------------# # Parameters # ------------------------------------------------------------# VPCCIDR: Default: 172.30.0.0/16 Type: String PublicSubnet01CIDR: Default: 172.30.1.0/24 Type: String PrivateSubnet01CIDR: Default: 172.30.3.0/24 Type: String EC2VolumeSize: Default: 32 Type: Number EC2VolumeIOPS: Default: 3000 Type: Number EC2AMI: Default: ami-06ee4e2261a4dc5c3 Type: AWS::EC2::Image::Id EC2InstanceType: Default: t3.micro Type: String Resources: # ------------------------------------------------------------# # IAM # ------------------------------------------------------------# EC2IAMRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: - ec2.amazonaws.com Action: - 'sts:AssumeRole' ManagedPolicyArns: - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore RoleName: iam-role-ec2 Tags: - Key: Name Value: iam-role-ec2 EC2IAMInstanceProfile: Type: AWS::IAM::InstanceProfile Properties: InstanceProfileName: iam-instanceprofile-ec2 Roles: - !Ref EC2IAMRole # ------------------------------------------------------------# # VPC # ------------------------------------------------------------# VPC: Type: AWS::EC2::VPC Properties: CidrBlock: !Ref VPCCIDR EnableDnsSupport: true EnableDnsHostnames: true Tags: - Key: Name Value: account-a-vpc # ------------------------------------------------------------# # InternetGateway # ------------------------------------------------------------# InternetGateway: Type: AWS::EC2::InternetGateway Properties: Tags: - Key: Name Value: account-a-igw InternetGatewayAttachment: Type: AWS::EC2::VPCGatewayAttachment Properties: InternetGatewayId: !Ref InternetGateway VpcId: !Ref VPC # ------------------------------------------------------------# # Subnet # ------------------------------------------------------------# PublicSubnet01: Type: AWS::EC2::Subnet Properties: AvailabilityZone: ap-northeast-1a CidrBlock: !Ref PublicSubnet01CIDR MapPublicIpOnLaunch: true Tags: - Key: Name Value: !Sub account-a-public-subnet-01 VpcId: !Ref VPC PrivateSubnet01: Type: AWS::EC2::Subnet Properties: AvailabilityZone: ap-northeast-1a CidrBlock: !Ref PrivateSubnet01CIDR MapPublicIpOnLaunch: false Tags: - Key: Name Value: !Sub account-a-private-subnet-01 VpcId: !Ref VPC # ------------------------------------------------------------# # NatGateWay # ------------------------------------------------------------# NatGateWayEIP: Type: AWS::EC2::EIP Properties: Domain: vpc Tags: - Key: Name Value: eip-natgw NatGateWay: Type: AWS::EC2::NatGateway Properties: AllocationId: !GetAtt NatGateWayEIP.AllocationId ConnectivityType: public SubnetId: !Ref PublicSubnet01 Tags: - Key: Name Value: account-a-natgw-1a # ------------------------------------------------------------# # RouteTable # ------------------------------------------------------------# PublicRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC Tags: - Key: Name Value: account-a-public-rtb PublicRouteTableRoute: Type: AWS::EC2::Route Properties: DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref InternetGateway RouteTableId: !Ref PublicRouteTable PublicRtAssociation1: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref PublicRouteTable SubnetId: !Ref PublicSubnet01 PrivateRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC Tags: - Key: Name Value: account-a-private-rtb PrivateRouteTableRoute: Type: AWS::EC2::Route Properties: DestinationCidrBlock: 0.0.0.0/0 NatGatewayId: !Ref NatGateWay RouteTableId: !Ref PrivateRouteTable PrivateRtAssociation1: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref PrivateRouteTable SubnetId: !Ref PrivateSubnet01 # ------------------------------------------------------------# # Security Group # ------------------------------------------------------------# EC2SG: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: for ec2 GroupName: account-a-sg-ec2 SecurityGroupEgress: - CidrIp: 0.0.0.0/0 FromPort: -1 IpProtocol: -1 ToPort: -1 Tags: - Key: Name Value: account-a-sg-ec2 VpcId: !Ref VPC # ------------------------------------------------------------# # EC2 # ------------------------------------------------------------# EC2: Type: AWS::EC2::Instance Properties: BlockDeviceMappings: - DeviceName: /dev/xvda Ebs: DeleteOnTermination: true Encrypted: true Iops: !Ref EC2VolumeIOPS VolumeSize: !Ref EC2VolumeSize VolumeType: gp3 DisableApiTermination: false IamInstanceProfile: !Ref EC2IAMInstanceProfile ImageId: !Ref EC2AMI InstanceType: !Ref EC2InstanceType NetworkInterfaces: - DeleteOnTermination: true DeviceIndex: 0 GroupSet: - !Ref EC2SG SubnetId: !Ref PrivateSubnet01 Tags: - Key: Name Value: test-ec2 UserData: !Base64 | #!/bin/bash yum remove mariadb-libs rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-2022 yum localinstall https://dev.mysql.com/get/mysql80-community-release-el7-6.noarch.rpm -y yum install -y mysql-community-client Outputs: # ------------------------------------------------------------# # Outputs # ------------------------------------------------------------# VPCID: Value: !Ref VPC Export: Name: VPC PublicSubnet01ID: Value: !Ref PublicSubnet01 Export: Name: PublicSubnet01ID PrivateSubnet01ID: Value: !Ref PrivateSubnet01 Export: Name: PrivateSubnet01ID PrivateRouteTableID: Value: !Ref PrivateRouteTable Export: Name: PrivateRouteTableID
EC2はMySQLクライアントをインストールするために「UserData」でインストールコマンドを記載しています。
デプロイは以下のコマンドを実行します。
VPCのCIDRなどを変更したい場合はコマンドのオプションで「--parameters」を入れてください。
aws cloudformation create-stack --stack-name CloudFormationスタック名 --template-body file://CloudFormationテンプレートファイル名 --capabilities CAPABILITY_NAMED_IAM
アカウントBでリソースの作成
アカウントBではRDS Aurora MySQLをシングルAZで起動します。
まずはネットワーク部分とクロスアカウント用のIAMロールを作成してVPCピアリングをできるようにしていきます。
IAMロールは以下の公式ドキュメントと同じものになります。
チュートリアル: 別の AWS アカウント の VPC とピア接続する
リソース作成には以下のCloudFormationテンプレートを使用しました。
CloudFormationテンプレート (ここをクリックしてください)
AWSTemplateFormatVersion: "2010-09-09" Description: Network Stack Metadata: # ------------------------------------------------------------# # Metadata # ------------------------------------------------------------# AWS::CloudFormation::Interface: ParameterGroups: - Label: default: Parameters for IAM Parameters: - PeerRequesterAccountId - Label: default: Parameters for VPC Parameters: - VPCCIDR - Label: default: Parameters for Subnet Parameters: - PrivateSubnet01CIDR - PrivateSubnet02CIDR Parameters: # ------------------------------------------------------------# # Parameters # ------------------------------------------------------------# PeerRequesterAccountId: Type: Number Description: AWS Account ID VPCCIDR: Default: 172.17.0.0/16 Type: String PrivateSubnet01CIDR: Default: 172.17.1.0/24 Type: String PrivateSubnet02CIDR: Default: 172.17.2.0/24 Type: String Resources: # ------------------------------------------------------------# # IAM # ------------------------------------------------------------# peerRole: Type: 'AWS::IAM::Role' Properties: AssumeRolePolicyDocument: Statement: - Principal: AWS: !Ref PeerRequesterAccountId Action: - 'sts:AssumeRole' Effect: Allow Path: / Policies: - PolicyName: root PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: 'ec2:AcceptVpcPeeringConnection' Resource: '*' # ------------------------------------------------------------# # VPC # ------------------------------------------------------------# VPC: Type: AWS::EC2::VPC Properties: CidrBlock: !Ref VPCCIDR EnableDnsSupport: true EnableDnsHostnames: true Tags: - Key: Name Value: account-b-vpc # ------------------------------------------------------------# # Subnet # ------------------------------------------------------------# PrivateSubnet01: Type: AWS::EC2::Subnet Properties: AvailabilityZone: ap-northeast-1a CidrBlock: !Ref PrivateSubnet01CIDR MapPublicIpOnLaunch: false Tags: - Key: Name Value: !Sub account-b-private-subnet-01 VpcId: !Ref VPC PrivateSubnet02: Type: AWS::EC2::Subnet Properties: AvailabilityZone: ap-northeast-1c CidrBlock: !Ref PrivateSubnet02CIDR MapPublicIpOnLaunch: false Tags: - Key: Name Value: !Sub account-b-private-subnet-02 VpcId: !Ref VPC # ------------------------------------------------------------# # RouteTable # ------------------------------------------------------------# PrivateRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC Tags: - Key: Name Value: account-b-private-rtb PrivateRtAssociation1: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref PrivateRouteTable SubnetId: !Ref PrivateSubnet01 PrivateRtAssociation2: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref PrivateRouteTable SubnetId: !Ref PrivateSubnet02 Outputs: # ------------------------------------------------------------# # Outputs # ------------------------------------------------------------# RoleARN: Value: !GetAtt peerRole.Arn Export: Name: RoleARN VPCID: Value: !Ref VPC Export: Name: VPC PrivateSubnet01ID: Value: !Ref PrivateSubnet01 Export: Name: PrivateSubnet01ID PrivateSubnet02ID: Value: !Ref PrivateSubnet02 Export: Name: PrivateSubnet02ID PrivateRouteTableID: Value: !Ref PrivateRouteTable Export: Name: PrivateRouteTableID
こちらのCloudFormationテンプレートではプライベートサブネットを2つ作成しています。
デプロイは以下のコマンドを実行します。
こちらのCloudFormationはアカウントBで実行するものなので実行場所 (クレデンシャル) などに気を付けてください。
aws cloudformation create-stack --stack-name CloudFormationスタック名 --template-body file://CloudFormationテンプレートファイル名 --parameters ParameterKey=PeerRequesterAccountId,ParameterValue=AWSアカウントAのID --capabilities CAPABILITY_NAMED_IAM
アカウントAでVPCピアリングの作成
ここからはアカウントAでVPCピアリングを作成していきます。
リソース作成には以下のCloudFormationテンプレートを使用しました。
AWSTemplateFormatVersion: "2010-09-09" Description: Network Stack Metadata: # ------------------------------------------------------------# # Metadata # ------------------------------------------------------------# AWS::CloudFormation::Interface: ParameterGroups: - Label: default: Parameters for VPC Peering Parameters: - PeerOwnerId - PeerRegion - PeerRoleArn - PeerVpcId Parameters: # ------------------------------------------------------------# # Parameters # ------------------------------------------------------------# PeerOwnerId: Type: Number Description: AWS Account-b ID PeerRegion: Default: ap-northeast-1 Type: String PeerRoleArn: Type: String Description: Account-b Role ARN PeerVpcId: Type: String Description: Account-b VPC ID Resources: # ------------------------------------------------------------# # VPC Peering # ------------------------------------------------------------# VPCPeering: Type: AWS::EC2::VPCPeeringConnection Properties: PeerOwnerId: !Ref PeerOwnerId PeerRegion: !Ref PeerRegion PeerRoleArn: !Ref PeerRoleArn PeerVpcId: !Ref PeerVpcId VpcId: !ImportValue VPC # ------------------------------------------------------------# # RouteTable # ------------------------------------------------------------# PrivateRouteTableRoute: Type: AWS::EC2::Route Properties: DestinationCidrBlock: 172.17.0.0/16 VpcPeeringConnectionId: !Ref VPCPeering RouteTableId: !ImportValue PrivateRouteTableID
こちらのCloudFormationテンプレートではVPCピアリングの作成とアカウントBのVPC向けの経路をルートテーブルに登録しています。
「PeerRoleArn」はクロスアカウントの場合は必要ですが、同一アカウント内であればIAMロールは不要になります。
デプロイは以下のコマンドを実行します。
aws cloudformation create-stack --stack-name CloudFormationスタック名 --template-body file://CloudFormationテンプレートファイル名 --parameters ParameterKey=PeerOwnerId,ParameterValue=AWSアカウントBのID ParameterKey=PeerRoleArn,ParameterValue=AWSアカウントBで作成したIAMロールのARN ParameterKey=PeerVpcId,ParameterValue=AWSアカウントBのVPC ID
アカウントBでRDS Aurora MySQLの作成
ここではアカウントBでRDS Aurora MySQLの作成を行います。
リソース作成には以下のCloudFormationテンプレートを使用しました。
CloudFormationテンプレート (ここをクリックしてください)
AWSTemplateFormatVersion: "2010-09-09" Description: RDS Stack Metadata: # ------------------------------------------------------------# # Metadata # ------------------------------------------------------------# AWS::CloudFormation::Interface: ParameterGroups: - Label: default: Parameters for Security Group Parameters: - EC2SG - SourceSecurityGroupOwnerId - Label: default: Parameters for RDS Aurora MySQL Parameters: - InstanceClass - AuroraMySQLVersion Parameters: # ------------------------------------------------------------# # Parameters # ------------------------------------------------------------# EC2SG: Type: String Description: SecurityGroup ID SourceSecurityGroupOwnerId: Type: Number Description: AWS Account-b ID VpcPeeringConnectionId: Type: String Description: VPC Peering ID InstanceClass: Default: db.t3.medium Type: String AuroraMySQLVersion: Default: 8.0.mysql_aurora.3.02.2 Type: String Resources: # ------------------------------------------------------------# # Security Group # ------------------------------------------------------------# RDSSG: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: for aurora mysql GroupName: account-b-sg-aurora-mysql SecurityGroupEgress: - CidrIp: 0.0.0.0/0 FromPort: -1 IpProtocol: -1 ToPort: -1 SecurityGroupIngress: - FromPort: 3306 IpProtocol: tcp SourceSecurityGroupId: !Ref EC2SG ToPort: 3306 Tags: - Key: Name Value: account-b-sg-aurora-mysql VpcId: !ImportValue VPC # ------------------------------------------------------------# # RouteTable # ------------------------------------------------------------# PrivateRouteTableRoute: Type: AWS::EC2::Route Properties: DestinationCidrBlock: 172.16.0.0/16 VpcPeeringConnectionId: !Ref VpcPeeringConnectionId RouteTableId: !ImportValue PrivateRouteTableID # ------------------------------------------------------------# # RDS Password # ------------------------------------------------------------# Password: Type: AWS::SecretsManager::Secret Properties: Description: RDS Password GenerateSecretString: GenerateStringKey: password SecretStringTemplate: '{"username": "root"}' ExcludeCharacters: '"@/\' Name: password-aurora-mysql Tags: - Key: Name Value: password-aurora-mysql # ------------------------------------------------------------# # RDS Subnet Group # ------------------------------------------------------------# SubnetGroup: Type: AWS::RDS::DBSubnetGroup Properties: DBSubnetGroupDescription: for aurora mysql subnet group DBSubnetGroupName: subng-aurora-mysql SubnetIds: - !ImportValue PrivateSubnet01ID - !ImportValue PrivateSubnet02ID Tags: - Key: Name Value: subng-aurora-mysql # ------------------------------------------------------------# # RDS Cluster # ------------------------------------------------------------# Cluster: Type: AWS::RDS::DBCluster Properties: BackupRetentionPeriod: 1 CopyTagsToSnapshot: true DBClusterIdentifier: account-b-aurora-mysql DBSubnetGroupName: !Ref SubnetGroup DeletionProtection: false Engine: aurora-mysql EngineVersion: !Ref AuroraMySQLVersion MasterUsername: !Sub '{{resolve:secretsmanager:${Password}::username}}' MasterUserPassword: !Sub '{{resolve:secretsmanager:${Password}::password}}' Port: 3306 StorageEncrypted: true Tags: - Key: Name Value: account-b-aurora-mysql VpcSecurityGroupIds: - !Ref RDSSG # ------------------------------------------------------------# # RDS Instance # ------------------------------------------------------------# Instance: Type: AWS::RDS::DBInstance Properties: AutoMinorVersionUpgrade: true AvailabilityZone: ap-northeast-1a DBClusterIdentifier: !Ref Cluster DBInstanceClass: !Ref InstanceClass DBInstanceIdentifier: account-b-aurora-mysql-1 Engine: aurora-mysql PubliclyAccessible: false Tags: - Key: Name Value: account-b-aurora-mysql-1
セキュリティグループではアカウントAのEC2からポート番号3306でアクセスしてこれるようにアカウントAのEC2が使用しているセキュリティグループを送信元として設定します。
クロスアカウントのセキュリティグループ参照については以下の公式ドキュメントをご確認ください。
セキュリティグループの更新とピアセキュリティグループの参照
RDS Aurora MySQLのマスターユーザー、パスワードはSecrets Managerに格納されるようにしています。
デプロイは以下のコマンドを実行します。
aws cloudformation create-stack --stack-name CloudFormationスタック名 --template-body file://CloudFormationテンプレートファイル名 --parameters ParameterKey=EC2SG,ParameterValue=アカウントAにあるEC2のセキュリティグループID ParameterKey=SourceSecurityGroupOwnerId,ParameterValue=アカウントAのID
デプロイが完了したら以下のコマンドを実行してRDS Aurora MySQLのマスターユーザーのパスワードを確認します。
get-secret-value --secret-id Secrets ManagerのARN --query SecretString
実行すると以下のような結果が返ってきます。
"{\"password\":\"RDS Aurora MySQLのパスワード\",\"username\":\"root\"}"
動作確認
アカウントAのEC2に接続して以下のコマンドを実行してください
パスワードは上記手順で確認したものになります。
mysql -h RDS Aurora MySQLのライターエンドポイント -u root -P 3306 -p
接続ができれば成功です。
さいごに
クロスアカウントでのVPCピアリング設定は初めてやりましたが詰まる部分も無くサクサク進められました。
アプリケーションを乗せたVPCと管理用のVPCで分けたりするときに使えるので手順は覚えておいて損はないといった印象です。